/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Data;

import Gramaticas.lex;
import Gramaticas.lex2;
import Gramaticas.parser;
import Gramaticas.parser2;
import Principal.VentanaErrores;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author rjimenez
 */
public class Tabla {

    HashMap<String,Simbolo> tablaSimbolos;
    ListaError listaErrores;
    int posicionPila;
    int posicionHeap;
    HashMap<String,File> listaImports;


    public Tabla() {

        this.tablaSimbolos = new HashMap<String, Simbolo>();
        this.listaErrores = new ListaError();
        this.posicionPila = 1;
        this.posicionHeap = 0;
        this.listaImports = new HashMap<String, File>();

    }

    public ListaError getListaErrores() {
        return listaErrores;
    }

    public void setListaErrores(ListaError listaErrores) {
        this.listaErrores = listaErrores;
    }

    public HashMap<String, Simbolo> getTablaSimbolos() {
        return tablaSimbolos;
    }

    public void setTablaSimbolos(HashMap<String, Simbolo> tablaSimbolos) {
        this.tablaSimbolos = tablaSimbolos;
    }

    

    public void resetearPila()
    {
        this.posicionPila =1;
    }

    public void resetearHeap()
    {
        this.posicionHeap = 0;
    }

    public void setPosicionPila(int posicionPila) {
        this.posicionPila = posicionPila;
    }
    
    

    public void agregarSimbolo(Simbolo nuevo, Token token)
    {
        

        if(!tablaSimbolos.containsKey(nuevo.getNombre()))
        {
            tablaSimbolos.put(nuevo.getNombre(), nuevo);

        }
        else
        {
            
            listaErrores.AgregarErrorSemantico(token, "La variable ya existe");
        }

        
    }

    public void agregarVariables(String ambito, String visibilidad, String tipo, String rol, int local, ArrayList<Token> vars)
    {
        String nombre;
        Simbolo nuevo;

        for(Token variable: vars)
        {
            nombre = ambito+"_"+variable.getLexema();

            if(!tablaSimbolos.containsKey(nombre))
            {
                if(local ==0)
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.GLOBAL);
                    nuevo.setPosicion(posicionHeap);
                    posicionHeap++;
                    tablaSimbolos.put(nombre, nuevo);

                }
                else
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.LOCAL);
                    nuevo.setPosicion(posicionPila);
                    posicionPila++;
                    tablaSimbolos.put(nombre, nuevo);
                    
                }


            }
            else
            {
                listaErrores.AgregarErrorSemantico(variable, "La variable ya existe");
            }
        }
    }
    
    public void agregarVariable(String ambito, String visibilidad, String tipo, String rol, int local, Token variable)
    {
        String nombre;
        Simbolo nuevo;
        
        nombre = ambito+"_"+variable.getLexema();

            if(!tablaSimbolos.containsKey(nombre))
            {
                if(local ==0)
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.GLOBAL);
                    nuevo.setPosicion(posicionHeap);
                    posicionHeap++;
                    tablaSimbolos.put(nombre, nuevo);

                }
                else
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.LOCAL);
                    nuevo.setPosicion(posicionPila);
                    posicionPila++;
                    tablaSimbolos.put(nombre, nuevo);
                    
                }


            }
            else
            {
                listaErrores.AgregarErrorSemantico(variable, "La variable ya existe");
            }
    }
    
    public void agregarVariable(String ambito, String visibilidad, String tipo, String rol, int local, String variable)
    {
        String nombre;
        Simbolo nuevo;
        
        nombre = variable;

            if(!tablaSimbolos.containsKey(nombre))
            {
                if(local ==0)
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.GLOBAL);
                    nuevo.setPosicion(posicionHeap);
                    posicionHeap++;
                    tablaSimbolos.put(nombre, nuevo);

                }
                else
                {
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(tipo);
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(rol);
                    nuevo.setVisibilidad(visibilidad);
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.LOCAL);
                    nuevo.setPosicion(posicionPila);
                    posicionPila++;
                    tablaSimbolos.put(nombre, nuevo);
                    
                }


            }
            
    }

    public void agregarArreglos(Token variable, String ambito, String visibilidad, String tipo, String rol, int local,int tamaño, int numDimensiones, String dimensiones)
    {

        Simbolo nuevo;
        String nombre = ambito+"_"+variable.getLexema();

        if(!tablaSimbolos.containsKey(nombre))
        {
            if(local ==0)
            {
                nuevo = new Simbolo();
                nuevo.setNombre(nombre);
                nuevo.setTipo(tipo);
                nuevo.setAmbito(ambito);
                nuevo.setRol(rol);
                nuevo.setVisibilidad(visibilidad);
                nuevo.setTamaño(tamaño);
                nuevo.setTipoVariable(Tipos.GLOBAL);
                nuevo.setPosicion(posicionHeap);
                nuevo.setTam_dimensiones(dimensiones);
                nuevo.setDimensiones(numDimensiones);
                posicionHeap=posicionHeap+tamaño;
                tablaSimbolos.put(nombre, nuevo);

            }
            else
            {
                nuevo = new Simbolo();
                nuevo.setNombre(nombre);
                nuevo.setTipo(tipo);
                nuevo.setAmbito(ambito);
                nuevo.setRol(rol);
                nuevo.setVisibilidad(visibilidad);
                nuevo.setTamaño(tamaño);
                nuevo.setTipoVariable(Tipos.LOCAL);
                nuevo.setPosicion(posicionPila);
                nuevo.setTam_dimensiones(dimensiones);
                nuevo.setDimensiones(numDimensiones);
                posicionPila=posicionPila+tamaño;
                tablaSimbolos.put(nombre, nuevo);

            }

        }

    }

    public void agregarParametros(String ambito, ArrayList<Parametro> lista)
    {

        String nombre;
        Simbolo nuevo;
        Token variable;

        for(Parametro parametro: lista)
        {
            variable =  parametro.getParametro();
            nombre = ambito+"_"+variable.getLexema();

            if(!tablaSimbolos.containsKey(nombre))
            {
                
                    nuevo = new Simbolo();
                    nuevo.setNombre(nombre);
                    nuevo.setTipo(parametro.getTipo());
                    nuevo.setAmbito(ambito);
                    nuevo.setRol(Tipos.PARAMETRO);
                    nuevo.setVisibilidad(Tipos.PRIVADA);
                    nuevo.setReferencia(parametro.getReferencia());
                    nuevo.setTamaño(1);
                    nuevo.setTipoVariable(Tipos.LOCAL);
                    nuevo.setPosicion(posicionPila);
                    posicionPila++;
                    tablaSimbolos.put(nombre, nuevo);




            }
            else
            {
                listaErrores.AgregarErrorSemantico(variable, "La variable ya existe");
            }
        }

    }

    public void agregarFunciones(Token variable, String ambito, String visibilidad, String tipo, String params)
    {
        Simbolo nuevo;
        String nombre;
        
        if(variable.getLexema().compareTo("principal")==0)
        {
            nombre = variable.getLexema();
        }
        else if(params.compareTo("")==0)
        {
            nombre= ambito+"_"+variable.getLexema();
        }
        else
        {
            nombre= ambito+"_"+variable.getLexema()+params;
        }
        

        if(!tablaSimbolos.containsKey(nombre))
        {
            
                nuevo = new Simbolo();
                nuevo.setNombre(nombre);
                nuevo.setTipo(tipo);
                nuevo.setAmbito(ambito);
                nuevo.setRol(Tipos.METODO);
                nuevo.setVisibilidad(visibilidad);
                nuevo.setTamaño(posicionPila); 
                nuevo.setParametros(params);
                tablaSimbolos.put(nombre, nuevo);



        }
        else
        {
                listaErrores.AgregarErrorSemantico(variable, "El metodo ya existe");
        }
    }
        
    public void agregarClases(Token variable)
    {
        String nombre = variable.getLexema();
        Simbolo nuevo;
        
       if(!tablaSimbolos.containsKey(nombre))
       {
            nuevo = new Simbolo();
            nuevo.setNombre(nombre);
            nuevo.setTipo(Tipos.CLASE);
            nuevo.setAmbito("");
            nuevo.setRol(Tipos.CLASE);
            nuevo.setVisibilidad(Tipos.PUBLICA);
            nuevo.setTamaño(posicionHeap);
            tablaSimbolos.put(nombre, nuevo);
       }
       else
       {
            listaErrores.AgregarErrorSemantico(variable, "La clase ya existe");
            
       }
    }

    public void modificarTamañoClase(Token variable)
    {
        String nombre = variable.getLexema();
        Simbolo  clase= tablaSimbolos.get(nombre);
        clase.setTamaño(posicionHeap);
    }
    
    public DefaultTableModel obtenerTabla()
    {
        String [] titulos= {"Nombre","Tipo","Ambito","Rol","Visibilidad","Tamaño","Posicion","Dimensiones","Parametros","Tamaño Dimensiones","Referencia","Hereda de"};
        DefaultTableModel dtm = new DefaultTableModel(null,titulos);
        Simbolo sim;
        

        for (Map.Entry e : tablaSimbolos.entrySet()) {

            sim = (Simbolo)e.getValue();
            
            Object data[] ={sim.getNombre(), sim.getTipo(),sim.getAmbito(),sim.getRol(),sim.getVisibilidad(),sim.getTamaño(),sim.getPosicion(),sim.getDimensiones(),sim.getParametros(),sim.getTam_dimensiones(),sim.getReferencia(),sim.getHeredaDe()};
            dtm.addRow(data);
        }

        
        
        return dtm;
    }

    public Simbolo obtenerSimbolo(String ambito, Token variable)
    {
        String nombre;
        String ambitoPadre;
        
        nombre = ambito+"_"+variable.getLexema();

        if(tablaSimbolos.containsKey(nombre))//dentro del ambito actual
        {
            return tablaSimbolos.get(nombre);
        }
        else
        {
            if(tablaSimbolos.containsKey(ambito))//buscamos la funcion
            {
                Simbolo sim = tablaSimbolos.get(ambito);
                ambitoPadre = sim.getAmbito();
                nombre = ambitoPadre+"_"+variable.getLexema();

                if(tablaSimbolos.containsKey(nombre))
                {
                    return tablaSimbolos.get(nombre);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
    }
    
    public Simbolo obtenerSimbolo(String nombre)
    {
        return tablaSimbolos.get(nombre);
        
    }

    public String obtenerTipoVariable(String ambito, Token variable)
    {
        Simbolo sim = obtenerSimbolo(ambito, variable);

        if(sim!=null)
        {
            return sim.getTipo();
        }
        else
        {
            return Tipos.ERRORTIPO;
        }
    }
    
    public Simbolo obtenerPropiedadObjeto(String ambito, Token objeto, Token variable)
    {
        Simbolo sim = obtenerSimbolo(ambito, objeto);
        
        if(sim!=null)
        {
            String nombre = sim.getNombre()+"_"+variable.getLexema();
            
            if(tablaSimbolos.containsKey(nombre))
            {
                return tablaSimbolos.get(nombre);
            }
            else
            {
                return null;
            }
        }
        
        
        
        return sim;
    }
    
    public String obtenerTipoPropiedadObjeto(String ambito, Token objeto, Token variable)
    {
        Simbolo sim = obtenerPropiedadObjeto(ambito,objeto,variable);

        if(sim!=null)
        {
            return sim.getTipo();
        }
        else
        {
            return Tipos.ERRORTIPO;
        }
    }
    
    public Simbolo obtenerFuncionObjeto(String ambito, Token objeto, Token variable, String parametros)
    {
        
        
        Simbolo sim = obtenerSimbolo(ambito, objeto);
        
        if(sim!=null)
        {
            String nombre = sim.getNombre()+"_"+variable.getLexema()+"_"+parametros;
            
            if(tablaSimbolos.containsKey(nombre))
            {
                return tablaSimbolos.get(nombre);
            }
            else
            {
                return null;
            }
        }
        
        
        
        return sim;
    }
    
    public String obtenerTipoFuncionObjeto(String ambito, Token objeto, Token variable, String parametros)
    {
        Simbolo sim = obtenerFuncionObjeto(ambito,objeto,variable,parametros);

        if(sim!=null)
        {
            return sim.getTipo();
        }
        else
        {
            return Tipos.ERRORTIPO;
        }
    }
    
    public Simbolo obtenerFuncion(String clase, Token funcion, String parametros)
    {
        String nombre = clase + funcion.getLexema()+"_"+parametros;
        
        if(tablaSimbolos.containsKey(nombre))
        {
            return tablaSimbolos.get(nombre);
        }
        else
        {
            return null;
        }
                
    }
    
    public String obtenerTipoFuncion(String clase, Token funcion, String parametros)
    {
        Simbolo sim = obtenerFuncion(clase, funcion, parametros);

        if(sim!=null)
        {
            return sim.getTipo();
        }
        else
        {
            return Tipos.ERRORTIPO;
        }
    }
    
    public void analizarImport(Token importe, String direccion)
    {
         String direccionImporte = importe.getLexema().replace("\"","");
         
         File archivoImporte;
         
         archivoImporte = new File(direccionImporte);
         
         if(archivoImporte.exists())
         {
             try {
                 //compilar 
                    parser miparser = new parser(new lex(new FileInputStream(archivoImporte)));
                    miparser.setDireccion(ObtenerPath(archivoImporte.getAbsolutePath(),archivoImporte.getName()));
                    miparser.setNombre(archivoImporte.getName());
                    miparser.parse();
                                        
                    tablaSimbolos.putAll(miparser.tablaSimbolos.getTablaSimbolos());
                    listaErrores.addTodos(miparser.tablaSimbolos.getListaErrores());
                    listaImports.put(direccionImporte, archivoImporte);
                    
                    

                    
             } catch (FileNotFoundException ex) {
                 Logger.getLogger(Tabla.class.getName()).log(Level.SEVERE, null, ex);
             } catch (Exception ex) {
                 Logger.getLogger(Tabla.class.getName()).log(Level.SEVERE, null, ex);
             }
         }
         else
         {
             archivoImporte = new File(direccion+direccionImporte);
             
             if(archivoImporte.exists())
             {
                 //compilar
                 try {
                 
                        parser miparser = new parser(new lex(new FileInputStream(archivoImporte)));
                        miparser.setDireccion(ObtenerPath(archivoImporte.getAbsolutePath(),archivoImporte.getName()));
                        miparser.setNombre(archivoImporte.getName());
                        miparser.parse();
                        tablaSimbolos.putAll(miparser.tablaSimbolos.getTablaSimbolos());
                        listaErrores.addTodos(miparser.tablaSimbolos.getListaErrores());
                        listaImports.put(direccionImporte, archivoImporte);
                        
//                        if(listaErrores.estaVacia())
//                        {
//                                parser2 parsersemantico = new parser2(new lex2(new FileInputStream(archivoImporte)));
//                                parsersemantico.agregarTablaSimbolos(miparser.tablaSimbolos);
//                                parsersemantico.parse();
//                                listaErrores.addTodos(parsersemantico.validar.getListaErrores());
//
//                                if(!listaErrores.estaVacia())
//                                {
//                                    JOptionPane.showMessageDialog(null,"Existe errores en el archivo compilado");
//                                    VentanaErrores ventError = new VentanaErrores(listaErrores);
//                                     ventError.setVisible(true);
//                                }
//                        }
                    
                 } catch (FileNotFoundException ex) {
                     Logger.getLogger(Tabla.class.getName()).log(Level.SEVERE, null, ex);
                 } catch (Exception ex) {
                     Logger.getLogger(Tabla.class.getName()).log(Level.SEVERE, null, ex);
                 }
             }
             else
             {
                 listaErrores.AgregarErrorSemantico(importe,"El archivo importado no existe");
             }
         }
    }
    
    public String ObtenerPath(String dire, String nombre)
    {
        String direccion;
        String diraux; 
        int tamañoDir = dire.length();
        int tamañoNom = nombre.length();
        
        diraux = dire.substring(0, tamañoDir - tamañoNom);
        direccion = diraux;
        
        return direccion;
    }
    
    public void agregarHerencia(Token padre, Token hijo)
    {
        Simbolo sim;
        String nombrePadre = padre.getLexema();
        String nombreHijo = hijo.getLexema();
        String nombreTemporal;
        String ambitoTemporal;
        ArrayList<Simbolo> listaHerdados = new ArrayList<Simbolo>();

        for (Map.Entry e : tablaSimbolos.entrySet()) {

            sim = (Simbolo)e.getValue();
            
            if(sim.getTipoVariable()==Tipos.GLOBAL)
            {
                if(sim.getNombre().startsWith(nombrePadre))
                {
                    
                    listaHerdados.add(sim);
                }
            }
            
        }
        
        for(Simbolo temporal : listaHerdados)
        {
            nombreTemporal = temporal.getNombre().replace(nombrePadre, nombreHijo);
            ambitoTemporal = temporal.getAmbito().replace(nombrePadre, nombreHijo);
            agregarVariable(ambitoTemporal,temporal.getVisibilidad(),temporal.getTipo(),temporal.getRol(),0,nombreTemporal);
        }
        
        listaHerdados.clear();
            
            
    }
        
        


}
